#!/usr/bin/env node

const fs = require('fs-extra');
const _ = require('lodash');
const turf = require('@turf/turf');

function writeJson(file, data) {
    fs.writeFileSync(file, JSON.stringify(data, null, 2));
}
function getRawArea(totalFile, jsonFile, distFile) {
    const data = fs.readJsonSync(jsonFile);
    const feature = data.features ? _.find(data.features, o=>o.geometry.type == 'Polygon') : data;

    const result = { type: 'FeatureCollection', features: [] };
    let index = 1;
    const total = fs.readJsonSync(totalFile);
    for (const geometry of total.geometries) {
        if (geometry.type == 'Polygon') {
            if (turf.booleanWithin(geometry, feature)) {
                result.features.push({
                    type: 'Feature',
                    geometry: { type: 'Polygon', coordinates: geometry.coordinates },
                    properties: { name: index++ },
                });
            }
        } else {
            let _coordinates = geometry.coordinates;
            for (const coordinates of _coordinates) {
                if (turf.booleanWithin(turf.polygon(coordinates), feature)) {
                    result.features.push({
                        type: 'Feature',
                        geometry: { type: 'Polygon', coordinates },
                        properties: { name: index++ },
                    });
                }
            }
        }
    }
    writeJson(distFile, result);
}
function get(jsonFile, origionName, distFile) {
    !distFile && (distFile = jsonFile);
    const data = fs.readJsonSync(jsonFile);
    data.features = _.filter(data.features, o=>o.properties.name == origionName);
    writeJson(distFile, data);
}
function deleteName(jsonFile, origionName, distFile) {
    !distFile && (distFile = jsonFile);
    const data = fs.readJsonSync(jsonFile);
    _.remove(data.features, o=>o.properties.name == origionName);
    writeJson(distFile, data);
}
function rename(jsonFile, origionName, distName, distFile) {
    !distFile && (distFile = jsonFile);
    const data = fs.readJsonSync(jsonFile);
    const feature = _.find(data.features, o=>o.properties.name == origionName);
    feature.properties.name = distName;
    writeJson(distFile, data);
}
function merge(jsonFile, name1, name2, distName, distFile) {
    !distFile && (distFile = jsonFile);
    const data = fs.readJsonSync(jsonFile);
    const features = data.features;
    let index1, index2;
    for (let i in features) {
        if (features[i].properties.name == name1) {
            index1 = +i;
        } else if (features[i].properties.name == name2) {
            index2 = +i;
        }
    }
    const union = turf.union(features[index1], features[index2]);
    union.properties.name = distName;
    features[index1] = union;
    features.splice(index2, 1);
    writeJson(distFile, data);
}
function mergeAll(jsonFile, names, distName, distFile) {
    !distFile && (distFile = jsonFile);
    names = names.split(',');
    const length = names.length;
    if (length == 1) {
        rename(jsonFile, names[0], distName, distFile)
    } else {
        console.log("[merge]:", jsonFile, names[0], names[1], distName);
        merge(jsonFile, names[0], names[1], distName, distFile);
        for (let i=2; i<length; i++) {
            console.log("[merge]:", jsonFile, distName, names[i], distName);
            merge(jsonFile, distName, names[i], distName, distFile);
        }
    }
}
function concatFeatures(jsonFiles, distFile) {
    jsonFiles = jsonFiles.split(',');
    !distFile && (distFile = jsonFiles[0]);
    const length = jsonFiles.length;
    const data = fs.readJsonSync(jsonFiles[0]);
    for (let i=1; i<length; i++) {
        const _data = fs.readJsonSync(jsonFiles[i]);
        data.features = data.features.concat(_data.features);
    }
    writeJson(distFile, data);
}
function multi2polygon(jsonFile, distFile) {
    !distFile && (distFile = jsonFile);
    const data = fs.readJsonSync(jsonFile);
    let index = 0;
    const _features = [];
    for (const feature of data.features) {
        if (feature.geometry.type === 'MultiPolygon') {
            const coordinates = feature.geometry.coordinates;
            for (const coordinate of coordinates) {
                _features.push({
                    type: 'Feature',
                    geometry: { type: 'Polygon', coordinates: coordinate },
                    properties: { name: `${feature.properties.name}${(index++)||''}` },
                });
            }
        }
    }
    data.features = _features;
    writeJson(distFile, data);
}
function translate(jsonFile, x, y, distFile) {
    x = +x;
    y = +y;
    !distFile && (distFile = jsonFile);
    let data = fs.readJsonSync(jsonFile);
    const length = (x < 0 ? -1 : 1) * Math.sqrt(x*x+y*y);
    const angle = (y < 0 ? -1 : 1) * Math.atan(Math.abs(x/y))/Math.PI*180;
    console.log("=======", { x, y, length,  angle });
    data = turf.transformTranslate(data, length, angle, { units: 'meters', mutate: true });
    writeJson(distFile, data);
}
// 这个方法不好，因为intersect会失真
//-s: 源文件 源区域名称 分割区域名称 包含部分名称 不同部分名称 [目标文件]
//xmap -s 1.json origion new intersectName differenceName 2.json
function splitByPolygon(jsonFile, srcPolygonName, splitPolygonName, intersectName, differenceName, distFile) {
    !distFile && (distFile = jsonFile);
    const data = fs.readJsonSync(jsonFile);
    const features = data.features;
    let index1, index2;
    for (let i in features) {
        if (features[i].properties.name == srcPolygonName) {
            index1 = +i;
        } else if (features[i].properties.name == splitPolygonName) {
            index2 = +i;
        }
    }
    const intersect = turf.intersect(features[index1], features[index2]);
    intersect.properties.name = intersectName;
    const difference = turf.difference(features[index1], features[index2]);
    difference.properties.name = differenceName;
    features.splice(_.max([index1, index2]), 1);
    features.splice(_.min([index1, index2]), 1);
    features.push(intersect);
    features.push(difference);
    writeJson(distFile, data);
}
function lineCoordinatesJoin(line1, line2) {
    line1.geometry.coordinates = line1.geometry.coordinates.concat(line2.geometry.coordinates);
    return line1;
}
function lineJoin(line1, line2) {
    const coords1 = turf.getCoords(line1);
    const coords2 = turf.getCoords(line2);
    const point10 = _.first(coords1);
    const point20 = _.first(coords2);
    const point21 = _.last(coords2);
    const len1 = turf.distance(point20, point10);
    const len2 = turf.distance(point21, point10);
    if (len1 < len2) {
        line1.geometry.coordinates = line1.geometry.coordinates.concat(_.reverse([...line2.geometry.coordinates]));
    } else {
        line1.geometry.coordinates = line1.geometry.coordinates.concat(line2.geometry.coordinates);
    }
    return turf.lineToPolygon(line1);
}
function split(jsonFile, distFile) {
    !distFile && (distFile = jsonFile);
    const data = fs.readJsonSync(jsonFile);
    let polygons = _.filter(data.features, o=>o.geometry.type === 'Polygon');
    const lines = _.filter(data.features, o=>o.geometry.type == 'LineString');
    for (const line of lines) {
        const _polygons = [];
        for (const polygon of polygons) {
            const pline = turf.polygonToLine(polygon);
            const intersect = turf.lineIntersect(pline, line);
            if (intersect.features.length !== 2) {
                _polygons.push(polygon);
                continue;
            }
            const psplit = turf.lineSplit(pline, line);
            const lsplit = turf.lineSplit(line, pline);
            const polygonLine1 = psplit.features[1];
            const polygonLine2 = lineCoordinatesJoin(psplit.features[2], psplit.features[0]);
            const splitLine = lsplit.features[1];
            const polygon1 = lineJoin(polygonLine1, splitLine);
            const polygon2 = lineJoin(polygonLine2, splitLine);
            _polygons.push(polygon1);
            _polygons.push(polygon2);
        }
        polygons = _polygons;
    }
    writeJson(distFile, turf.featureCollection(polygons));
}
function showList(jsonFile) {
    const data = fs.readJsonSync(jsonFile);
    const features = data.features;
    features.forEach((o, k)=>{
        console.log(`${k}: ${o.properties.name} (${o.geometry.type})`);
    });
}
function help() {
    console.log('帮助:');
    console.log('   拆分：http://www.dnccn.com/mapchaifen.html');
    console.log('   画图：http://geojson.io');
    console.log('   简化：https://mapshaper.org/');
    console.log('   获取县级数据：http://datav.aliyun.com/tools/atlas/index.html');
    console.log();
    console.log('   -m: 源文件 源区域名称1,源区域名称2,... 合并的名称 [目标文件]');
    console.log('   合并多个区域，如果源区域只有一个，则为重命名');
    console.log('   xmap -m 1.json name1,name2 distName 2.json');
    console.log();
    console.log('   -s: 源文件 [目标文件]');
    console.log('   使用多条分割线分割出不同的部分');
    console.log('   xmap -s 1.json 2.json');
    console.log();
    console.log('   -r: 源文件 源区域名称 修改后的名称 [目标文件]');
    console.log('   重命名一个区域');
    console.log('   xmap -r 1.json origionName distName 2.json');
    console.log();
    console.log('   -d: 源文件 源区域名称 [目标文件]');
    console.log('   删除一个区域');
    console.log('   xmap -d 1.json origionName 2.json');
    console.log();
    console.log('   -g: 源文件 源区域名称 [目标文件]');
    console.log('   获取单独一个区域');
    console.log('   xmap -g 1.json origionName 2.json');
    console.log();
    console.log('   -raw: 总文件 分割文件 [目标文件]');
    console.log('   从总文件用分割区域分割出县级区域, box.json可以使用四边形框处一个县的区域');
    console.log('   xmap -raw total.json box.json 2.json');
    console.log();
    console.log('   -concat: 源文件1,源文件2,... [目标文件]');
    console.log('   链接文件');
    console.log('   xmap -concat 1.json,2.json 3.json');
    console.log();
    console.log('   -multi2polygon: 源文件 [目标文件]');
    console.log('   将MultiPolygon转化为单个的Polygon');
    console.log('   xmap -multi2polygon 1.json 2.json');
    console.log();
    console.log('   -translate: 源文件 x y [目标文件]');
    console.log('   将原来的区域按照x y的数值设置偏移,单位为米');
    console.log('   xmap -translate 1.json 100 -100 2.json');
    console.log();
    console.log('   -l: 源文件');
    console.log('   查看源文件的列表');
    console.log('   xmap -l 1.json');
}
function main(args) {
    if (args[0] == '-h') {
        return help();
    }
    if (args[0] == '-m') {
        return mergeAll(...args.slice(1));
    }
    if (args[0] == '-s') {
        return split(...args.slice(1));
    }
    if (args[0] == '-r') {
        return rename(...args.slice(1));
    }
    if (args[0] == '-d') {
        return deleteName(...args.slice(1));
    }
    if (args[0] == '-g') {
        return get(...args.slice(1));
    }
    if (args[0] == '-raw') {
        return getRawArea(...args.slice(1));
    }
    if (args[0] == '-concat') {
        return concatFeatures(...args.slice(1));
    }
    if (args[0] == '-multi2polygon') {
        return multi2polygon(...args.slice(1));
    }
    if (args[0] == '-translate') {
        return translate(...args.slice(1));
    }
    if (args[0] == '-l') {
        return showList(...args.slice(1));
    }
}

main(process.argv.slice(2));
